#include "FileExplorer.h"
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/aui/aui.h>

#include <sdk.h>

#ifndef CB_PRECOMP

#include <wx/dnd.h>
#include <wx/imaglist.h>

#include <cbproject.h>
#include <configmanager.h>
#include <projectmanager.h>

#endif

#include <list>
#include <vector>
#include <iostream>

#include "se_globals.h"
#include "CommitBrowser.h"

#include <wx/arrimpl.cpp> // this is a magic incantation which must be done!

WX_DEFINE_OBJARRAY(VCSstatearray);

int ID_UPDATETIMER = wxNewId();
int ID_FILETREE = wxNewId();
int ID_FILELOC = wxNewId();
int ID_FILEWILD = wxNewId();
int ID_SETLOC = wxNewId();
int ID_VCSCONTROL = wxNewId();
int ID_VCSTYPE = wxNewId();
int ID_VCSCHANGESCHECK = wxNewId();

int ID_OPENINED = wxNewId();
int ID_FILENEWFILE = wxNewId();
int ID_FILENEWFOLDER = wxNewId();
int ID_FILEMAKEFAV = wxNewId();
int ID_FILECOPY = wxNewId();
int ID_FILEDUP = wxNewId();
int ID_FILEMOVE = wxNewId();
int ID_FILEDELETE = wxNewId();
int ID_FILERENAME = wxNewId();
int ID_FILEEXPANDALL = wxNewId();
int ID_FILECOLLAPSEALL = wxNewId();
int ID_FILESETTINGS = wxNewId();
int ID_FILESHOWHIDDEN = wxNewId();
int ID_FILEPARSECVS = wxNewId();
int ID_FILEPARSESVN = wxNewId();
int ID_FILEPARSEHG = wxNewId();
int ID_FILEPARSEBZR = wxNewId();
int ID_FILEPARSEGIT = wxNewId();
int ID_FILE_UPBUTTON = wxNewId();
int ID_FILEREFRESH = wxNewId();
int ID_FILEADDTOPROJECT = wxNewId();

int ID_FILEDIFF = wxNewId();
/*为文件差异项保留10个附加ID*/
int ID_FILEDIFF1 = wxNewId();
int ID_FILEDIFF2 = wxNewId();
int ID_FILEDIFF3 = wxNewId();
int ID_FILEDIFF4 = wxNewId();
int ID_FILEDIFF5 = wxNewId();
int ID_FILEDIFF6 = wxNewId();
int ID_FILEDIFF7 = wxNewId();
int ID_FILEDIFF8 = wxNewId();
int ID_FILEDIFF9 = wxNewId();
int ID_FILEDIFF10 = wxNewId();

class UpdateQueue {
public:
    void Add(const wxTreeItemId &ti) {
        for (std::list<wxTreeItemId>::iterator it = qdata.begin(); it != qdata.end(); it++) {
            if (*it == ti) {
                qdata.erase(it);
                break;
            }
        }
        qdata.push_front(ti);
    }

    bool Pop(wxTreeItemId &ti) {
        if (qdata.empty())
            return false;
        ti = qdata.front();
        qdata.pop_front();
        return true;
    }

    void Clear() {
        qdata.clear();
    }

private:
    std::list<wxTreeItemId> qdata;
};


class DirTraverseFind : public wxDirTraverser {
public:
    DirTraverseFind(const wxString &wildcard) : m_files(), m_wildcard(wildcard) {}

    virtual wxDirTraverseResult OnFile(const wxString &filename) {
        if (WildCardListMatch(m_wildcard, filename, true))
            m_files.Add(filename);
        return wxDIR_CONTINUE;
    }

    virtual wxDirTraverseResult OnDir(const wxString &dirname) {
        if (WildCardListMatch(m_wildcard, dirname, true))
            m_files.Add(dirname);
        return wxDIR_CONTINUE;
    }

    wxArrayString &GetMatches() { return m_files; }

private:
    wxArrayString m_files;
    wxString m_wildcard;
};


class FEDataObject : public wxDataObjectComposite {
public:
    FEDataObject() : wxDataObjectComposite() {
        m_file = new wxFileDataObject;
        Add(m_file, true);
    }

    wxFileDataObject *m_file;

};


class wxFEDropTarget : public wxDropTarget {
public:
    wxFEDropTarget(FileExplorer *fe) : wxDropTarget() {
        m_fe = fe;
        m_data_object = new FEDataObject();
        SetDataObject(m_data_object);
    }

    virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) {
        GetData();
        if (m_data_object->GetReceivedFormat().GetType() == wxDF_FILENAME) {
            wxArrayString as = m_data_object->m_file->GetFilenames();
            wxTreeCtrl *tree = m_fe->m_Tree;
            int flags;
            wxTreeItemId id = tree->HitTest(wxPoint(x, y), flags);
            if (!id.IsOk())
                return wxDragCancel;
            if (tree->GetItemImage(id) != fvsFolder)
                return wxDragCancel;
            if (!(flags & (wxTREE_HITTEST_ONITEMICON | wxTREE_HITTEST_ONITEMLABEL)))
                return wxDragCancel;
            if (def == wxDragCopy) {
                m_fe->CopyFiles(m_fe->GetFullPath(id), as);
                return def;
            }
            if (def == wxDragMove) {
                m_fe->MoveFiles(m_fe->GetFullPath(id), as);
                return def;
            }
            return wxDragCancel;
        }
//            if(sizeof(wxFileDataObject)!=m_data_object->GetDataSize(wxDF_FILENAME))
//            {
//                wxMessageBox(wxString::Format(wxT("Drop files %i,%i"),sizeof(wxFileDataObject),m_data_object->GetDataSize(wxDF_FILENAME)));
//                return wxDragCancel;
//            }
        return wxDragCancel;
    }

    virtual bool OnDrop(wxCoord /*x*/, wxCoord /*y*/, int /*tab*/, wxWindow */*wnd*/) {
        return true;
    }

    virtual wxDragResult OnDragOver(wxCoord /*x*/, wxCoord /*y*/, wxDragResult def) {
        return def;
    }

private:
    FEDataObject *m_data_object;
    FileExplorer *m_fe;
};


BEGIN_EVENT_TABLE(FileTreeCtrl, wxTreeCtrl)
//双击
//EVT_TREE_ITEM_ACTIVATED(ID_FILETREE, FileTreeCtrl::OnActivate)
                EVT_KEY_DOWN(FileTreeCtrl::OnKeyDown)
END_EVENT_TABLE()

IMPLEMENT_DYNAMIC_CLASS(FileTreeCtrl, wxTreeCtrl)

FileTreeCtrl::FileTreeCtrl(wxWindow *parent, wxWindowID id, const wxPoint &pos,
                           const wxSize &size, long style,
                           const wxValidator &validator,
                           const wxString &name)
        : wxTreeCtrl(parent, id, pos, size, style, validator, name) {}

FileTreeCtrl::FileTreeCtrl() {}

FileTreeCtrl::FileTreeCtrl(wxWindow *parent) : wxTreeCtrl(parent) {}

FileTreeCtrl::~FileTreeCtrl() {
}

void FileTreeCtrl::OnKeyDown(wxKeyEvent &event) {
    if (event.GetKeyCode() == WXK_DELETE)
        ::wxPostEvent(GetParent(), event);
    else
        event.Skip(true);
}

int FileTreeCtrl::OnCompareItems(const wxTreeItemId &item1, const wxTreeItemId &item2) {
    if ((GetItemImage(item1) == fvsFolder) > (GetItemImage(item2) == fvsFolder))
        return -1;
    if ((GetItemImage(item1) == fvsFolder) < (GetItemImage(item2) == fvsFolder))
        return 1;
    if ((GetItemImage(item1) == fvsVcNonControlled) < (GetItemImage(item2) == fvsVcNonControlled))
        return -1;
    if ((GetItemImage(item1) == fvsVcNonControlled) < (GetItemImage(item2) == fvsVcNonControlled))
        return 1;
    return (GetItemText(item1).CmpNoCase(GetItemText(item2)));
}

BEGIN_EVENT_TABLE(FileExplorer, wxPanel)
                EVT_TIMER(ID_UPDATETIMER, FileExplorer::OnTimerCheckUpdates)
                EVT_MONITOR_NOTIFY(wxID_ANY, FileExplorer::OnDirMonitor)
                EVT_COMMAND(0, wxEVT_NOTIFY_UPDATE_COMPLETE, FileExplorer::OnUpdateTreeItems)
                EVT_COMMAND(0, wxEVT_NOTIFY_LOADER_UPDATE_COMPLETE, FileExplorer::OnVCSFileLoaderComplete)
//    EVT_COMMAND(0, wxEVT_NOTIFY_EXEC_REQUEST, FileExplorer::OnExecRequest)
                EVT_TREE_BEGIN_DRAG(ID_FILETREE, FileExplorer::OnBeginDragTreeItem)
                EVT_TREE_END_DRAG(ID_FILETREE, FileExplorer::OnEndDragTreeItem)
                EVT_BUTTON(ID_FILE_UPBUTTON, FileExplorer::OnUpButton)
                EVT_MENU(ID_SETLOC, FileExplorer::OnSetLoc)
                EVT_MENU(ID_OPENINED, FileExplorer::OnOpenInEditor)
                EVT_MENU(ID_FILENEWFILE, FileExplorer::OnNewFile)
                EVT_MENU(ID_FILENEWFOLDER, FileExplorer::OnNewFolder)
                EVT_MENU(ID_FILEMAKEFAV, FileExplorer::OnAddFavorite)
                EVT_MENU(ID_FILECOPY, FileExplorer::OnCopy)
                EVT_MENU(ID_FILEDUP, FileExplorer::OnDuplicate)
                EVT_MENU(ID_FILEMOVE, FileExplorer::OnMove)
                EVT_MENU(ID_FILEDELETE, FileExplorer::OnDelete)
                EVT_MENU(ID_FILERENAME, FileExplorer::OnRename)
                EVT_MENU(ID_FILEEXPANDALL, FileExplorer::OnExpandAll)
                EVT_MENU(ID_FILECOLLAPSEALL, FileExplorer::OnCollapseAll)
                EVT_MENU(ID_FILESETTINGS, FileExplorer::OnSettings)
                EVT_MENU(ID_FILESHOWHIDDEN, FileExplorer::OnShowHidden)
                EVT_MENU(ID_FILEPARSECVS, FileExplorer::OnParseCVS)
                EVT_MENU(ID_FILEPARSESVN, FileExplorer::OnParseSVN)
                EVT_MENU(ID_FILEPARSEHG, FileExplorer::OnParseHG)
                EVT_MENU(ID_FILEPARSEBZR, FileExplorer::OnParseBZR)
                EVT_MENU(ID_FILEPARSEGIT, FileExplorer::OnParseGIT)
                EVT_MENU(ID_FILEREFRESH, FileExplorer::OnRefresh)
                EVT_MENU(ID_FILEADDTOPROJECT, FileExplorer::OnAddToProject)
                EVT_MENU_RANGE(ID_FILEDIFF, ID_FILEDIFF + 10, FileExplorer::OnVCSDiff)
                EVT_KEY_DOWN(FileExplorer::OnKeyDown)
                EVT_TREE_ITEM_EXPANDING(ID_FILETREE, FileExplorer::OnExpand)
                //删除子项
                //EVT_TREE_ITEM_COLLAPSED(id, func)
                //双击 - 打开文件/展开目录(后者是默认的刚需event.skip)
                EVT_TREE_ITEM_ACTIVATED(ID_FILETREE, FileExplorer::OnActivate)
                //右键点击打开上下文菜单 -- 解释器操作、重命名、删除、复制、属性、设置为根等
                EVT_TREE_ITEM_MENU(ID_FILETREE, FileExplorer::OnRightClick)
                //从组合框的历史记录中选择的位置-设置为根
                EVT_COMBOBOX(ID_FILELOC, FileExplorer::OnChooseLoc)
                EVT_COMBOBOX(ID_FILEWILD, FileExplorer::OnChooseWild)
                //在组合框中为目录名提供自动图文集提示
                //EVT_TEXT(ID_FILELOC, FileExplorer::OnLocChanging)
                //在组合框中输入的位置-设置为根
                EVT_TEXT_ENTER(ID_FILELOC, FileExplorer::OnEnterLoc)
                //在组合框中输入的位置-设置为根  (虫子缠身)
                EVT_TEXT_ENTER(ID_FILEWILD, FileExplorer::OnEnterWild)
                EVT_CHOICE(ID_VCSCONTROL, FileExplorer::OnVCSControl)
                EVT_CHECKBOX(ID_VCSCHANGESCHECK, FileExplorer::OnVCSChangesCheck)
END_EVENT_TABLE()

FileExplorer::FileExplorer(wxWindow *parent, wxWindowID id,
                           const wxPoint &pos, const wxSize &size,
                           long style, const wxString &name) :
        wxPanel(parent, id, pos, size, style, name) {
    m_kill = false;
    m_update_queue = new UpdateQueue;
    m_updater = NULL;
    m_updatetimer = new wxTimer(this, ID_UPDATETIMER);
    m_update_active = false;
    m_updater_cancel = false;
    m_update_expand = false;
    m_dir_monitor = new wxDirectoryMonitor(this, wxArrayString());
    m_dir_monitor->Start();
    m_droptarget = new wxFEDropTarget(this);

    m_show_hidden = false;
    m_parse_cvs = false;
    m_parse_hg = false;
    m_parse_bzr = false;
    m_parse_git = false;
    m_parse_svn = false;
    m_vcs_file_loader = 0;
    wxBoxSizer *bs = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer *bsh = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer *bshloc = new wxBoxSizer(wxHORIZONTAL);
    m_Box_VCS_Control = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer *box_vcs_top = new wxBoxSizer(wxHORIZONTAL);
    m_Tree = new FileTreeCtrl(this, ID_FILETREE);
    m_Tree->SetIndent(m_Tree->GetIndent() / 2);
    m_Tree->SetDropTarget(m_droptarget);
    m_Loc = new wxComboBox(this, ID_FILELOC, _T(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxTE_PROCESS_ENTER | wxCB_DROPDOWN);
    m_WildCards = new wxComboBox(this, ID_FILEWILD, _T(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxTE_PROCESS_ENTER | wxCB_DROPDOWN);
    m_UpButton = new wxButton(this, ID_FILE_UPBUTTON, wxT("^"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
    bshloc->Add(m_Loc, 1, wxEXPAND);
    bshloc->Add(m_UpButton, 0, wxEXPAND);
    bs->Add(bshloc, 0, wxEXPAND);
    bsh->Add(new wxStaticText(this, wxID_ANY, wxT("掩码: ")), 0, wxALIGN_CENTRE);
    bsh->Add(m_WildCards, 1);
    bs->Add(bsh, 0, wxEXPAND);

    m_VCS_Control = new wxChoice(this, ID_VCSCONTROL);
    m_VCS_Type = new wxStaticText(this, ID_VCSTYPE, _T(""));
    m_VCS_ChangesOnly = new wxCheckBox(this, ID_VCSCHANGESCHECK, wxT("仅显示更改的文件"));
    box_vcs_top->Add(m_VCS_Type, 0, wxALIGN_CENTER);
    box_vcs_top->Add(m_VCS_Control, 1, wxEXPAND);
    m_Box_VCS_Control->Add(box_vcs_top, 0, wxEXPAND);
    m_Box_VCS_Control->Add(m_VCS_ChangesOnly, 0, wxEXPAND);
    m_Box_VCS_Control->Hide(true);
    bs->Add(m_Box_VCS_Control, 0, wxEXPAND);

    bs->Add(m_Tree, 1, wxEXPAND | wxALL);

    SetAutoLayout(TRUE);

    SetImages();
    ReadConfig();
    if (m_Loc->GetCount() > m_favdirs.GetCount()) {
        m_Loc->Select(m_favdirs.GetCount());
        m_root = m_Loc->GetString(m_favdirs.GetCount());
    } else {
        m_root = wxFileName::GetPathSeparator();
        m_Loc->Append(m_root);
        m_Loc->Select(0);
    }
    if (m_WildCards->GetCount() > 0)
        m_WildCards->Select(0);
    SetRootFolder(m_root);

    SetSizer(bs);
}

FileExplorer::~FileExplorer() {
    m_kill = true;
    m_updatetimer->Stop();
    delete m_dir_monitor;
    WriteConfig();
    UpdateAbort();
    delete m_update_queue;
    delete m_updatetimer;
}

/*设置为目录根*/
bool FileExplorer::SetRootFolder(wxString root) {
    UpdateAbort();
    if (root[root.Len() - 1] != wxFileName::GetPathSeparator())
        root = root + wxFileName::GetPathSeparator();
#ifdef __WXMSW__
    wxFileName fnroot=wxFileName(root);
    if(fnroot.GetVolume().IsEmpty())
    {
        fnroot.SetVolume(wxFileName(::wxGetCwd()).GetVolume());
        //(wxPATH_GET_VOLUME|wxPATH_GET_SEPARATOR)+fnroot.GetFullName();
        root=fnroot.GetFullPath();
    }
#endif
    wxDir dir(root);
    if (!dir.IsOpened()) {
        /*在这里处理错误-wxDir已经记录了一条错误消息,解释了失败的确切原因*/
        m_Loc->SetValue(m_root);
        return false;
    }
    m_root = root;
    m_VCS_Control->Clear();
    m_commit = wxEmptyString;
    m_VCS_Type->SetLabel(wxEmptyString);
    m_Box_VCS_Control->Hide(true);
    m_Loc->SetValue(m_root);
    m_Tree->DeleteAllItems();
    m_Tree->AddRoot(m_root, fvsFolder);
    m_Tree->SetItemHasChildren(m_Tree->GetRootItem());
    m_Tree->Expand(m_Tree->GetRootItem());
    Layout();

    return true;
/*return AddTreeItems(m_Tree->GetRootItem());*/

}

/*在选定根目录下的文件系统中查找文件*/
void FileExplorer::FindFile(const wxString &findfilename, const wxTreeItemId &ti) {
    wxString path = GetFullPath(ti);

    wxDir dir(path);

    if (!dir.IsOpened()) {
        /*在这里处理错误-wxDir已经记录了一条错误消息,解释了失败的确切原因*/
        return;
    }
    wxString filename;
    int flags = wxDIR_FILES | wxDIR_DIRS;
    if (m_show_hidden)
        flags |= wxDIR_HIDDEN;

    DirTraverseFind dtf(findfilename);
    m_findmatchcount = dir.Traverse(dtf, wxEmptyString, flags);
    m_findmatch = dtf.GetMatches();
}

// focus the item in the tree.
// 将在树中项目设置选中(焦点).
void FileExplorer::FocusFile(const wxTreeItemId &ti) {
    m_Tree->SetFocus();
    m_Tree->UnselectAll();
    m_Tree->SelectItem(ti);
    m_Tree->EnsureVisible(ti);
}

/*获取下一个展开节点*/
wxTreeItemId FileExplorer::GetNextExpandedNode(wxTreeItemId ti) {
    wxTreeItemId next_ti;
    if (!ti.IsOk()) {
        return m_Tree->GetRootItem();
    }
    if (m_Tree->IsExpanded(ti)) {
        wxTreeItemIdValue cookie;
        next_ti = m_Tree->GetFirstChild(ti, cookie);
        while (next_ti.IsOk()) {
            if (m_Tree->IsExpanded(next_ti))
                return next_ti;
            next_ti = m_Tree->GetNextChild(ti, cookie);
        }
    }
    next_ti = m_Tree->GetNextSibling(ti);
    while (next_ti.IsOk()) {
        if (m_Tree->IsExpanded(next_ti))
            return next_ti;
        next_ti = m_Tree->GetNextSibling(next_ti);
    }
    return m_Tree->GetRootItem();
}

/*目录在目录书中*/
bool FileExplorer::GetItemFromPath(const wxString &path, wxTreeItemId &ti) {
    ti = m_Tree->GetRootItem();
    do {
        if (path == GetFullPath(ti))
            return true;
        ti = GetNextExpandedNode(ti);
    } while (ti != m_Tree->GetRootItem());
    return false;
}

/*展开节点*/
void FileExplorer::GetExpandedNodes(wxTreeItemId ti, Expansion *exp) {
    exp->name = m_Tree->GetItemText(ti);
    wxTreeItemIdValue cookie;
    wxTreeItemId ch = m_Tree->GetFirstChild(ti, cookie);
    while (ch.IsOk()) {
        if (m_Tree->IsExpanded(ch)) {
            Expansion *e = new Expansion();
            GetExpandedNodes(ch, e);
            exp->children.push_back(e);
        }
        ch = m_Tree->GetNextChild(ti, cookie);
    }
}

/*展开路径*/
void FileExplorer::GetExpandedPaths(wxTreeItemId ti, wxArrayString &paths) {
    if (!ti.IsOk()) {
        wxMessageBox(wxT("节点错误"));
        return;
    }
    if (m_Tree->IsExpanded(ti))
        paths.Add(GetFullPath(ti));
    wxTreeItemIdValue cookie;
    wxTreeItemId ch = m_Tree->GetFirstChild(ti, cookie);
    while (ch.IsOk()) {
        if (m_Tree->IsExpanded(ch))
            GetExpandedPaths(ch, paths);
        ch = m_Tree->GetNextChild(ti, cookie);
    }
}

void FileExplorer::RefreshExpanded(wxTreeItemId ti) {
    if (m_Tree->IsExpanded(ti))
        m_update_queue->Add(ti);
    wxTreeItemIdValue cookie;
    wxTreeItemId ch = m_Tree->GetFirstChild(ti, cookie);
    while (ch.IsOk()) {
        if (m_Tree->IsExpanded(ch))
            RefreshExpanded(ch);
        ch = m_Tree->GetNextChild(ti, cookie);
    }
    m_updatetimer->Start(10, true);

}

void FileExplorer::Refresh(wxTreeItemId ti) {
/*
Expansion e;
GetExpandedNodes(ti,&e);
RecursiveRebuild(ti,&e);
m_updating_node=ti;
m_Tree->GetRootItem();
*/
    m_update_queue->Add(ti);
    m_updatetimer->Start(10, true);
}

/*终止更新*/
void FileExplorer::UpdateAbort() {
    if (!m_update_active)
        return;
    delete m_updater;
    m_update_active = false;
    m_updatetimer->Stop();
}

/*复位目录监控*/
void FileExplorer::ResetDirMonitor() {
    wxArrayString paths;
    GetExpandedPaths(m_Tree->GetRootItem(), paths);
    m_dir_monitor->ChangePaths(paths);
}

/*响应目录监控*/
void FileExplorer::OnDirMonitor(wxDirectoryMonitorEvent &e) {
    if (m_kill) { return; }
    /*TODO:显然,在Code::Blocks关闭期间创建日志消息会创建段故障*/
    /*LogMessage(wxString::Format(_T("Dir Event: %s,%i,%s"),e.m_mon_dir.c_str(),e.m_event_type,e.m_info_uri.c_str()));*/

    LogMessage(wxString::Format(wxT("目录事件: %s,%i,%s"), e.m_mon_dir.c_str(), e.m_event_type, e.m_info_uri.c_str()));

    if (e.m_event_type == MONITOR_TOO_MANY_CHANGES) {
        /*LogMessage(wxT("目录更改读取错误,检测到太多的目录变化"));*/
        LogMessage(wxT("目录更改读取错误,检测到太多的目录变化"));
    }
    wxTreeItemId ti;
    if (GetItemFromPath(e.m_mon_dir, ti)) {
        m_update_queue->Add(ti);
        m_updatetimer->Start(100, true);
    }
}

void FileExplorer::OnTimerCheckUpdates(wxTimerEvent &/*e*/) {
    if (m_kill) { return; }
    if (m_update_active) { return; }

    wxTreeItemId ti;
    while (m_update_queue->Pop(ti)) {
        if (!ti.IsOk()) { continue; }

        m_updater_cancel = false;
        m_updater = new FileExplorerUpdater(this);
        m_updated_node = ti;
        m_update_active = true;
        m_updater->Update(m_updated_node);
        break;
    }
}

/*验证根目录*/
bool FileExplorer::ValidateRoot() {
    wxTreeItemId ti = m_Tree->GetRootItem();
    while (true) {
        if (!ti.IsOk()) { break; }
        if (m_Tree->GetItemImage(ti) != fvsFolder) { break; }
        /*对应目录不存在*/
        if (!wxFileName::DirExists(GetFullPath(ti))) { break; }
        return true;
    }
    return false;
}

void FileExplorer::OnUpdateTreeItems(wxCommandEvent &/*e*/) {
    if (m_kill) { return; }
    m_updater->Wait();
    wxTreeItemId ti = m_updated_node;
    bool viewing_commit = (m_updater->m_vcs_commit_string != wxEmptyString) && (m_updater->m_vcs_commit_string != wxT("工作副本"));
    if (ti == m_Tree->GetRootItem() && !viewing_commit) {
        m_VCS_Type->SetLabel(m_updater->m_vcs_type);
        if (m_updater->m_vcs_type == wxEmptyString) {
            m_VCS_Control->Clear();
            m_Box_VCS_Control->Hide(true);
            m_commit = _T("");
        } else if (m_commit == wxEmptyString) {
            m_VCS_Control->Clear();
            m_VCS_Control->Append(wxT("工作副本"));
            m_VCS_Control->Append(wxT("选择提交..."));
            m_VCS_Control->SetSelection(0);
            m_commit = wxT("工作副本");
            m_Box_VCS_Control->Show(true);
        }
        Layout();
    }
    if (m_updater_cancel || !ti.IsOk()) {
        /*节点被删除现在刷新!TODO:只需要清理并重新启动计时器(不需要更改队列)*/
        delete m_updater;
        m_updater = NULL;
        m_update_active = false;
        ResetDirMonitor();
        if (ValidateRoot()) {
            m_update_queue->Add(m_Tree->GetRootItem());
            m_updatetimer->Start(10, true);
        }
        return;
    }

    LogMessage(wxT("节点正常..."));
    /*m_Tree->DeleteChildren(ti);*/
    FileDataVec &removers = m_updater->m_removers;
    FileDataVec &adders = m_updater->m_adders;
    if (removers.size() > 0 || adders.size() > 0) {
        m_Tree->Freeze();
        /*遍历(清理)REMOVERS列表并从树中删除这些项*/
        LogMessage(wxT("移除节点对象集合(Removers)..."));
        for (FileDataVec::iterator it = removers.begin(); it != removers.end(); it++) {

            LogMessage(wxString::Format(wxT("节点名称:%s"), it->name));

            wxTreeItemIdValue cookie;
            wxTreeItemId ch = m_Tree->GetFirstChild(ti, cookie);
            while (ch.IsOk()) {
                if (it->name == m_Tree->GetItemText(ch)) {
                    m_Tree->Delete(ch);
                    break;
                }
                ch = m_Tree->GetNextChild(ti, cookie);
            }
        }

        /*遍历加法器列表并将这些项添加到树中*/
        LogMessage(wxT("添加节点对象集合(Adders)..."));

        for (FileDataVec::iterator it = adders.begin(); it != adders.end(); it++) {
            LogMessage(wxString::Format(wxT("节点名称:%s"), it->name));

            wxTreeItemId newitem = m_Tree->AppendItem(ti, it->name, it->state);
            m_Tree->SetItemHasChildren(newitem, it->state == fvsFolder);
        }
        m_Tree->SortChildren(ti);
        m_Tree->Thaw();
    }
    if (!m_Tree->IsExpanded(ti)) {
        m_update_expand = true;
        m_Tree->Expand(ti);
    }
    delete m_updater;
    m_updater = NULL;
    m_update_active = false;
    m_updatetimer->Start(10, true);
    /*重新启动(目录树)监视器(TODO:把这个移到别处??)*/
    ResetDirMonitor();
}

void FileExplorer::SetImages() {
    wxImageList *m_pImages = cbProjectTreeImages::MakeImageList();
    m_Tree->SetImageList(m_pImages);

/*
确保文件目录树没有被冻住
UnfreezeTree(true);
*/
}

wxString FileExplorer::GetFullPath(const wxTreeItemId &ti) {
    if (!ti.IsOk()) { return wxEmptyString; }

    wxFileName path(m_root);

    if (ti != m_Tree->GetRootItem()) {

        std::vector<wxTreeItemId> vti;
        vti.push_back(ti);
        wxTreeItemId pti = m_Tree->GetItemParent(vti[0]);

        if (!pti.IsOk()) { return wxEmptyString; }

        while (pti != m_Tree->GetRootItem()) {
            vti.insert(vti.begin(), pti);
            pti = m_Tree->GetItemParent(pti);
        }
        /*处理所选项可能是部分路径而不仅仅是文件名这一事实的复杂逻辑.这样做要简单得多*/
        for (size_t i = 0; i < vti.size() - 1; i++) { path.AppendDir(m_Tree->GetItemText(vti[i])); }

        wxFileName last_part(m_Tree->GetItemText(vti[vti.size() - 1]));
        wxArrayString as = last_part.GetDirs();

        for (size_t i = 0; i < as.size(); i++) { path.AppendDir(as[i]); }

        path = wxFileName(path.GetFullPath(), last_part.GetFullName()).GetFullPath();
    }
    return path.GetFullPath();
}

void FileExplorer::OnExpand(wxTreeEvent &event) {
    if (m_updated_node == event.GetItem() && m_update_expand) {
        m_update_expand = false;
        return;
    }
    m_update_queue->Add(event.GetItem());
    m_updatetimer->Start(10, true);
    event.Veto();
    /*AddTreeItems(event.GetItem());*/
}

/*读取文件资源管理器配置参数*/
void FileExplorer::ReadConfig() {
    /*从旧外壳扩展插件导入设置-TODO:在下一版本中删除*/
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("ShellExtensions"));

    if (!cfg->Exists(_("FileExplorer/ShowHidenFiles"))) { cfg = Manager::Get()->GetConfigManager(_T("FileManager")); }
    int len = 0;
    cfg->Read(_T("FileExplorer/FavRootList/Len"), &len);

    for (int i = 0; i < len; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/FavRootList/I%i"), i);
        wxString loc;
        FavoriteDir fav;
        cfg->Read(ref + _T("/alias"), &fav.alias);
        cfg->Read(ref + _T("/path"), &fav.path);
        m_Loc->Append(fav.alias);
        m_favdirs.Add(fav);
    }

    len = 0;
    cfg->Read(_T("FileExplorer/RootList/Len"), &len);
    for (int i = 0; i < len; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/RootList/I%i"), i);
        wxString loc;
        cfg->Read(ref, &loc);
        m_Loc->Append(loc);
    }

    len = 0;
    cfg->Read(_T("FileExplorer/WildMask/Len"), &len);
    for (int i = 0; i < len; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/WildMask/I%i"), i);
        wxString wild;
        cfg->Read(ref, &wild);
        m_WildCards->Append(wild);
    }

    cfg->Read(_T("FileExplorer/ParseCVS"), &m_parse_cvs);
    cfg->Read(_T("FileExplorer/ParseSVN"), &m_parse_svn);
    cfg->Read(_T("FileExplorer/ParseHG"), &m_parse_hg);
    cfg->Read(_T("FileExplorer/ParseBZR"), &m_parse_bzr);
    cfg->Read(_T("FileExplorer/ParseGIT"), &m_parse_git);
    cfg->Read(_T("FileExplorer/ShowHiddenFiles"), &m_show_hidden);
}

/*配置参数写入文件资源管理器配置文件*/
void FileExplorer::WriteConfig() {

    /*从旧外壳扩展插件导入设置*/
    /*TODO:在下一版本中删除*/
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("ShellExtensions"));

    if (cfg->Exists(_("FileExplorer/ShowHidenFiles"))) { cfg->DeleteSubPath(_("FileExplorer")); }

    cfg = Manager::Get()->GetConfigManager(_T("FileManager"));
    /*cfg->Clear();*/

    int count = static_cast<int>(m_favdirs.GetCount());

    LogMessage(wxString::Format(wxT("c1:%i"),count));

    cfg->Write(_T("FileExplorer/FavRootList/Len"), count);

    for (int i = 0; i < count; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/FavRootList/I%i"), i);
        cfg->Write(ref + _T("/alias"), m_favdirs[i].alias);
        cfg->Write(ref + _T("/path"), m_favdirs[i].path);
    }

    int c2=static_cast<int>(m_favdirs.GetCount());

    LogMessage(wxString::Format(wxT("c2:%i"),c2));
    int m1=static_cast<int>(m_Loc->GetCount());
    count = m1 - c2;
    cfg->Write(_T("FileExplorer/RootList/Len"), count);

    for (int i = 0; i < count; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/RootList/I%i"), i);
        int c3=static_cast<int>(m_favdirs.GetCount());

        LogMessage(wxString::Format(wxT("c3:%i"),c3));

        cfg->Write(ref, m_Loc->GetString(c3 + i));
    }

    count = static_cast<int>(m_Loc->GetCount());
    cfg->Write(_T("FileExplorer/WildMask/Len"), count);

    for (int i = 0; i < count; i++) {
        wxString ref = wxString::Format(_T("FileExplorer/WildMask/I%i"), i);
        cfg->Write(ref, m_WildCards->GetString(i));
    }

    cfg->Write(_T("FileExplorer/ParseCVS"), m_parse_cvs);
    cfg->Write(_T("FileExplorer/ParseSVN"), m_parse_svn);
    cfg->Write(_T("FileExplorer/ParseHG"), m_parse_hg);
    cfg->Write(_T("FileExplorer/ParseBZR"), m_parse_bzr);
    cfg->Write(_T("FileExplorer/ParseGIT"), m_parse_git);
    cfg->Write(_T("FileExplorer/ShowHiddenFiles"), m_show_hidden);
}

/*响应通配符写入*/
void FileExplorer::OnEnterWild(wxCommandEvent &/*event*/) {
    wxString wild = m_WildCards->GetValue();
    for (size_t i = 0; i < m_WildCards->GetCount(); i++) {
        wxString cmp;
        cmp = m_WildCards->GetString(i);
        if (cmp == wild) {
            m_WildCards->Delete(i);
            m_WildCards->Insert(wild, 0);
            m_WildCards->SetSelection(0);
            RefreshExpanded(m_Tree->GetRootItem());
            return;
        }
    }
    m_WildCards->Insert(wild, 0);
    if (m_WildCards->GetCount() > 10) { m_WildCards->Delete(10); }
    m_WildCards->SetSelection(0);
    RefreshExpanded(m_Tree->GetRootItem());
}

void FileExplorer::OnChooseWild(wxCommandEvent &/*event*/) {
    // 在win32上请注意,如果用户打开下拉列表,则键入通配符组合框事件将包含-1选择和空字符串项.在当前代码中是无害的.
    wxString wild = m_WildCards->GetValue();
    m_WildCards->Delete(m_WildCards->GetSelection());
    m_WildCards->Insert(wild, 0);
/*
event.Skip(true);
cbMessageBox(wild);
 */
    m_WildCards->SetSelection(0);
    RefreshExpanded(m_Tree->GetRootItem());
}

void FileExplorer::OnEnterLoc(wxCommandEvent &/*event*/) {
    wxString loc = m_Loc->GetValue();
    if (!SetRootFolder(loc)) { return; }

    for (size_t i = 0; i < m_Loc->GetCount(); i++) {
        wxString cmp;

        if (i < m_favdirs.GetCount()) { cmp = m_favdirs[i].path; } else { cmp = m_Loc->GetString(i); }

        if (cmp == m_root) {
            if (i >= m_favdirs.GetCount()) {
                m_Loc->Delete(i);
                m_Loc->Insert(m_root, m_favdirs.GetCount());
            }
            m_Loc->SetSelection(m_favdirs.GetCount());
            return;
        }
    }

    m_Loc->Insert(m_root, m_favdirs.GetCount());

    if (m_Loc->GetCount() > 10 + m_favdirs.GetCount()) { m_Loc->Delete(10 + m_favdirs.GetCount()); }

    m_Loc->SetSelection(m_favdirs.GetCount());
}

void FileExplorer::OnChooseLoc(wxCommandEvent &event) {
    wxString loc;
    // 在WIN32上,如果用户打开下拉列表,然后键入路径,则此事件将以空字符串触发,因此我们只能返回null.
    // 此事件不会在Linux上发生(当用户开始键入时,下拉列表关闭)
    if (event.GetInt() < 0) { return; }

    if (event.GetInt() >= static_cast<int>(m_favdirs.GetCount())) { loc = m_Loc->GetValue(); }
    else { loc = m_favdirs[event.GetInt()].path; }

    if (!SetRootFolder(loc)) { return; }

    if (event.GetInt() >= static_cast<int>(m_favdirs.GetCount())) {
        m_Loc->Delete(event.GetInt());
        m_Loc->Insert(m_root, m_favdirs.GetCount());
        m_Loc->SetSelection(m_favdirs.GetCount());
    } else {
        for (size_t i = m_favdirs.GetCount(); i < m_Loc->GetCount(); i++) {
            wxString cmp;
            cmp = m_Loc->GetString(i);
            if (cmp == m_root) {
                m_Loc->Delete(i);
                m_Loc->Insert(m_root, m_favdirs.GetCount());
                m_Loc->SetSelection(event.GetInt());
                return;
            }
        }
        m_Loc->Insert(m_root, m_favdirs.GetCount());

        if (m_Loc->GetCount() > 10 + m_favdirs.GetCount()) { m_Loc->Delete(10 + m_favdirs.GetCount()); }

        m_Loc->SetSelection(event.GetInt());
    }
}

void FileExplorer::OnSetLoc(wxCommandEvent &/*event*/) {
    /*SINGLE: m_Tree->GetSelection()*/
    wxString loc = GetFullPath(m_selectti[0]);
    if (!SetRootFolder(loc)) { return; }
    m_Loc->Insert(m_root, m_favdirs.GetCount());
    if (m_Loc->GetCount() > 10 + m_favdirs.GetCount()) { m_Loc->Delete(10 + m_favdirs.GetCount()); }
}

void FileExplorer::OnVCSChangesCheck(wxCommandEvent &/*event*/) {
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnVCSControl(wxCommandEvent &/*event*/) {
    int item_select_idx = m_VCS_Control->GetSelection();
    if (item_select_idx < 0) { item_select_idx = 0; }

    LogMessage(wxString::Format(wxT("OnVCSControl:选定项索引->%i"), item_select_idx));

    wxString commit = m_VCS_Control->GetString(item_select_idx);
    if (commit == wxT("选择提交...")) {
        CommitBrowser *cm = new CommitBrowser(this, GetFullPath(m_Tree->GetRootItem()), m_VCS_Type->GetLabel());
        if (cm->ShowModal() == wxID_OK) {
            commit = cm->GetSelectedCommit();
            cm->Destroy();
            if (commit != wxEmptyString) {
                unsigned int i = 0;
                for (; i < m_VCS_Control->GetCount(); ++i) {
                    if (m_VCS_Control->GetString(i) == commit) {
                        m_VCS_Control->SetSelection(i);
                        break;
                    }
                }
                if (i == m_VCS_Control->GetCount())
                    m_VCS_Control->Append(commit);
                m_VCS_Control->SetSelection(m_VCS_Control->GetCount() - 1);
            }
        } else
            commit = wxEmptyString;
    }
    if (commit != wxEmptyString) {
        m_commit = commit;
        Refresh(m_Tree->GetRootItem());
    } else {
        unsigned int i = 0;
        for (; i < m_VCS_Control->GetCount(); ++i) {
            if (m_VCS_Control->GetString(i) == m_commit) {
                m_VCS_Control->SetSelection(i);
                break;
            }
        }
    }
}

void FileExplorer::OnOpenInEditor(wxCommandEvent &/*event*/) {
    for (int i = 0; i < m_ticount; i++) {
        if (IsBrowsingVCSTree()) {
            wxFileName path(GetFullPath(m_selectti[i]));
            wxString original_path = path.GetFullPath();
            path.MakeRelativeTo(GetRootFolder());
            wxString name = path.GetFullName();
            wxString vcs_type = m_VCS_Type->GetLabel();
            name = vcs_type + _T("-") + m_commit.Mid(0, 6) + _T("-") + name;
            path.SetFullName(name);
            wxFileName tmp = wxFileName(wxFileName::GetTempDir(), _T(""));
            tmp.AppendDir(_T("codeblocks-fm"));
            path.MakeAbsolute(tmp.GetFullPath());

            if (!path.FileExists()) { m_vcs_file_loader_queue.Add(_T("cat"), original_path, path.GetFullPath()); }
            else { DoOpenInEditor(path.GetFullPath()); }

        } else {
            wxFileName path(GetFullPath(m_selectti[i]));
            wxString filename = path.GetFullPath();
            if (!path.FileExists()) { continue; }
            DoOpenInEditor(filename);
        }
    }
    if (m_vcs_file_loader == 0 && !m_vcs_file_loader_queue.empty()) {
        LoaderQueueItem item = m_vcs_file_loader_queue.Pop();
        m_vcs_file_loader = new VCSFileLoader(this);
        m_vcs_file_loader->Update(item.op, item.source, item.destination, item.comp_commit);
    }
}

void FileExplorer::OnVCSDiff(wxCommandEvent &event) {
    wxString comp_commit;

    if (event.GetId() == ID_FILEDIFF) {
        //与head(用于工作副本)或previous(用于提交)的差异
        comp_commit = _T("Previous");
    } else {
        //否则与具体版本不同
        comp_commit = m_VCS_Control->GetString(event.GetId() - ID_FILEDIFF1);
    }
    if (m_commit == wxT("工作副本") && comp_commit == wxT("工作副本")) { comp_commit = wxT("Previous"); }

    if (comp_commit == wxT("选择提交...")) {
        wxString diff_paths;
        for (int i = 0; i < m_ticount; i++) {
            wxFileName path(GetFullPath(m_selectti[i]));
            path.MakeRelativeTo(GetRootFolder());
            if (path != wxEmptyString) { diff_paths += _T(" \"") + path.GetFullPath() + _T("\""); }
        }

        CommitBrowser *cm = new CommitBrowser(this, GetFullPath(m_Tree->GetRootItem()), m_VCS_Type->GetLabel(), diff_paths);

        if (cm->ShowModal() == wxID_OK) {
            comp_commit = cm->GetSelectedCommit();
        } else { return; }
    }

    wxString diff_paths = wxEmptyString;
    for (int i = 0; i < m_ticount; i++) {
        wxFileName path(GetFullPath(m_selectti[i]));
        path.MakeRelativeTo(GetRootFolder());
        if (path != wxEmptyString) { diff_paths += _T(" \"") + path.GetFullPath() + _T("\""); }
    }
    wxFileName tmp = wxFileName(wxFileName::GetTempDir(), _T(""));
    wxFileName root_path(GetRootFolder());
    wxString name = root_path.GetName();
    wxString vcs_type = m_VCS_Type->GetLabel();
    tmp.AppendDir(_T("codeblocks-fm"));
    name = _T("diff-") + vcs_type + _T("-") + m_commit.Mid(0, 7) + _T("~") + comp_commit + _T("-") + name + _T(".patch");
    wxString dest_tmp_path = wxFileName(tmp.GetFullPath(), name).GetFullPath();
    m_vcs_file_loader_queue.Add(_T("diff"), diff_paths, dest_tmp_path, comp_commit);
    if (m_vcs_file_loader == 0 && !m_vcs_file_loader_queue.empty()) {
        LoaderQueueItem item = m_vcs_file_loader_queue.Pop();
        m_vcs_file_loader = new VCSFileLoader(this);
        m_vcs_file_loader->Update(item.op, item.source, item.destination, item.comp_commit);
    }
}

void FileExplorer::OnVCSFileLoaderComplete(wxCommandEvent & /*event*/) {
    m_vcs_file_loader->Wait();
    DoOpenInEditor(m_vcs_file_loader->m_destination_path);
    delete m_vcs_file_loader;
    m_vcs_file_loader = 0;
    if (!m_vcs_file_loader_queue.empty()) {
        LoaderQueueItem item = m_vcs_file_loader_queue.Pop();
        m_vcs_file_loader = new VCSFileLoader(this);
        m_vcs_file_loader->Update(item.op, item.source, item.destination, item.comp_commit);
    }
}

/*在内置编辑器打开文件*/
void FileExplorer::DoOpenInEditor(const wxString &filename) {
    EditorManager *em = Manager::Get()->GetEditorManager();
    EditorBase *eb = em->IsOpen(filename);
    if (eb) {
        /*打开的文件被激活*/
        eb->Activate();
        return;
    } else { em->Open(filename); }
}

void FileExplorer::OnActivate(wxTreeEvent &event) {
    if (IsBrowsingVCSTree()) {
        /*TODO:应该只检索文件并使用与常规文件相同的mime类型处理*/
        wxCommandEvent e;
        m_ticount = m_Tree->GetSelections(m_selectti);
        OnOpenInEditor(e);
        return;
    }
    wxString filename = GetFullPath(event.GetItem());
    if (m_Tree->GetItemImage(event.GetItem()) == fvsFolder) {
        event.Skip(true);
        return;
    }
    EditorManager *em = Manager::Get()->GetEditorManager();
    EditorBase *eb = em->IsOpen(filename);
    if (eb) {
        /*打开的文件被激活*/
        eb->Activate();
        return;
    }

    /*使用Mime处理器去打开文件*/
    cbMimePlugin *plugin = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile(filename);
    if (!plugin) {
        wxString msg;
        msg.Printf(wxT("无法打开文件'%s'.\n没有为此类文件注册处理程序."), filename.c_str());
        LogErrorMessage(msg);
        /*不需要从这里打开文件*/
        /*em->Open(filename);*/
    } else if (plugin->OpenFile(filename) != 0) {
        const PluginInfo *info = Manager::Get()->GetPluginManager()->GetPluginInfo(plugin);
        wxString msg;
        msg.Printf(wxT("无法打开文件 '%s'.\n注册的处理程序(%s)无法打开它."), filename.c_str(), info ? info->title.c_str() : wxString(wxT("<未知插件>")).c_str());
        LogErrorMessage(msg);
    }

//    if(!em->IsOpen(file)){em->Open(file);}
}


void FileExplorer::OnKeyDown(wxKeyEvent &event) {
    if (event.GetKeyCode() == WXK_DELETE) {
        if (IsBrowsingVCSTree()) {
            wxCommandEvent event2;
            OnDelete(event2);
        }
    }
}


bool FileExplorer::IsBrowsingVCSTree() {
    return m_commit != wxT("工作副本") && m_commit != wxEmptyString;
}

bool FileExplorer::IsBrowsingWorkingCopy() {
    return m_commit == wxT("工作副本") && m_commit != wxEmptyString;
}

/*右键点击处理上下文菜单*/
void FileExplorer::OnRightClick(wxTreeEvent &event) {
    wxMenu *Popup = new wxMenu();
    m_ticount = m_Tree->GetSelections(m_selectti);
    /*如果右键单击的项不在所选内容中,则将所选内容替换为右键单击的项*/
    if (!IsInSelection(event.GetItem())) {
        for (int i = 0; i < m_ticount; i++)
            m_Tree->SelectItem(m_selectti[i], false);
        m_Tree->SelectItem(event.GetItem());
        m_ticount = m_Tree->GetSelections(m_selectti);
        m_Tree->Update();
    }
    FileTreeData *ftd = new FileTreeData(0, FileTreeData::ftdkUndefined);
    ftd->SetKind(FileTreeData::ftdkFile);
    if (m_ticount > 0) {
        if (m_ticount == 1) {
            int img = m_Tree->GetItemImage(m_selectti[0]);
            if (img == fvsFolder) {
                ftd->SetKind(FileTreeData::ftdkFolder);
                Popup->Append(ID_SETLOC, wxT("以此目录为根目录"));
                /*TODO:在wx2.8中检查win32的可用性(wx2.6不可用)*/
                Popup->Append(ID_FILEEXPANDALL, wxT("展开所有子项"));
                /*TODO: 检查win32的wx2.8中的可用性(wx2.6不可用)*/
                Popup->Append(ID_FILECOLLAPSEALL, wxT("折叠所有子项"));
                if (!IsBrowsingVCSTree()) {
                    Popup->Append(ID_FILEMAKEFAV, wxT("添加到收藏夹"));
                    Popup->Append(ID_FILENEWFILE, wxT("新建文件..."));
                    Popup->Append(ID_FILENEWFOLDER, wxT("新建目录..."));
                    Popup->Append(ID_FILERENAME, wxT("重命名..."));
                }
            } else {
                if (!IsBrowsingVCSTree()) { Popup->Append(ID_FILERENAME, wxT("重命名...")); }
            }
        }
        if (IsFilesOnly(m_selectti)) {
            Popup->Append(ID_OPENINED, wxT("在CB编辑器中打开"));
            if (!IsBrowsingVCSTree())
                if (Manager::Get()->GetProjectManager()->GetActiveProject()) { Popup->Append(ID_FILEADDTOPROJECT, wxT("添加到活动项目...")); }
        }
        if (!IsBrowsingVCSTree()) {
            Popup->Append(ID_FILEDUP, wxT("复制"));
            Popup->Append(ID_FILECOPY, wxT("拷贝到..."));
            Popup->Append(ID_FILEMOVE, wxT("移动到..."));
            Popup->Append(ID_FILEDELETE, wxT("删除"));
        }
        if (IsBrowsingVCSTree() || IsBrowsingWorkingCopy()) {

            if (IsBrowsingWorkingCopy()) { Popup->Append(ID_FILEDIFF, wxT("差异")); }
            else { Popup->Append(ID_FILEDIFF, wxT("前一个差异")); }

            wxMenu *diff_menu = new wxMenu();
            unsigned int n = m_VCS_Control->GetCount();
            if (n > 10)
                n = 10;
            if (IsBrowsingWorkingCopy()) {
                diff_menu->Append(ID_FILEDIFF1, wxT("头部"));
                for (unsigned int i = 1; i < n; ++i) { diff_menu->Append(ID_FILEDIFF1 + i, m_VCS_Control->GetString(i)); }
            } else {
                for (unsigned int i = 0; i < n; ++i) { diff_menu->Append(ID_FILEDIFF1 + i, m_VCS_Control->GetString(i)); }
            }
            Popup->AppendSubMenu(diff_menu, wxT("反向检测差异"));
        }
    }
    wxMenu *viewpop = new wxMenu();
    viewpop->Append(ID_FILESETTINGS, wxT("收藏夹目录..."));
    viewpop->AppendCheckItem(ID_FILESHOWHIDDEN, wxT("显示隐藏文件"))->Check(m_show_hidden);
    /*viewpop->AppendCheckItem(ID_FILEPARSECVS,wxT("CVS decorators"))->Check(m_parse_cvs);*/
    viewpop->AppendCheckItem(ID_FILEPARSESVN, wxT("SVN集成"))->Check(m_parse_svn);
    viewpop->AppendCheckItem(ID_FILEPARSEHG, wxT("Hg集成"))->Check(m_parse_hg);
    viewpop->AppendCheckItem(ID_FILEPARSEBZR, wxT("Bzr集成"))->Check(m_parse_bzr);
    viewpop->AppendCheckItem(ID_FILEPARSEGIT, wxT("Git集成"))->Check(m_parse_git);
    if (m_ticount > 1) {
        ftd->SetKind(FileTreeData::ftdkVirtualGroup);
        wxString pathlist = GetFullPath(m_selectti[0]);

        /*将以"*"分隔的文件/目录列表传递给任何插件接受者*/
        for (int i = 1; i < m_ticount; i++) { pathlist += _T("*") + GetFullPath(m_selectti[i]); }

        ftd->SetFolder(pathlist);
    } else if (m_ticount > 0) {
        wxString filepath = GetFullPath(m_selectti[0]);
        ftd->SetFolder(filepath);
    }

    if (m_ticount > 0) { Manager::Get()->GetPluginManager()->AskPluginsForModuleMenu(mtUnknown, Popup, ftd); }

    delete ftd;
    Popup->AppendSeparator();
    Popup->AppendSubMenu(viewpop, wxT("设置"));
    Popup->Append(ID_FILEREFRESH, wxT("刷新"));
    wxWindow::PopupMenu(Popup);
    delete Popup;
}

/*新建文件*/
void FileExplorer::OnNewFile(wxCommandEvent &/*event*/) {
    //SINGLE: m_Tree->GetSelection()
    wxString workingdir = GetFullPath(m_selectti[0]);
    wxTextEntryDialog te(this, wxT("命名新文件: "));

    if (te.ShowModal() != wxID_OK) { return; }

    wxString name = te.GetValue();
    wxFileName file(workingdir);
    file.Assign(file.GetFullPath(), name);
    wxString newfile = file.GetFullPath();
    if (!wxFileName::FileExists(newfile) && !wxFileName::DirExists(newfile)) {
        wxFile fileobj;
        if (fileobj.Create(newfile)) {
            fileobj.Close();
            //SINGLE: m_Tree->GetSelection()
            Refresh(m_selectti[0]);
        } else { cbMessageBox(wxT("文件创建失败"), wxT("错误")); }

    } else { cbMessageBox(wxT("名为 ") + name + wxT("的文件或目录已存在"), wxT("错误")); }
}

void FileExplorer::OnAddFavorite(wxCommandEvent &/*event*/) {
    FavoriteDir fav;
    fav.path = GetFullPath(m_selectti[0]);

    if (fav.path[fav.path.Len() - 1] != wxFileName::GetPathSeparator()) { fav.path = fav.path + wxFileName::GetPathSeparator(); }

    wxTextEntryDialog ted(NULL, wxT("输入此目录的别名:"), wxT("添加收藏目录"), fav.path);

    if (ted.ShowModal() != wxID_OK) { return; }

    wxString name = ted.GetValue();
    fav.alias = name;
    m_favdirs.Insert(fav, 0);
    m_Loc->Insert(name, 0);
}

void FileExplorer::OnNewFolder(wxCommandEvent &/*event*/) {
    //SINGLE: m_Tree->GetSelection()
    wxString workingdir = GetFullPath(m_selectti[0]);
    wxTextEntryDialog te(this, wxT("新目录名: "));

    if (te.ShowModal() != wxID_OK) { return; }

    wxString name = te.GetValue();
    wxFileName dir(workingdir);
    dir.Assign(dir.GetFullPath(), name);
    wxString mkd = dir.GetFullPath();
    if (!wxFileName::DirExists(mkd) && !wxFileName::FileExists(mkd)) {
        if (!dir.Mkdir(mkd)) { cbMessageBox(wxT("无法创建名为 ") + name + wxT("的目录")); }
        //SINGLE: m_Tree->GetSelection()
        Refresh(m_selectti[0]);
    } else { cbMessageBox(wxT("名为的 ") + name + wxT("文件或目录已存在")); }
}

void FileExplorer::OnDuplicate(wxCommandEvent &/*event*/) {
    m_ticount = m_Tree->GetSelections(m_selectti);
    for (int i = 0; i < m_ticount; i++) {
        //SINGLE: m_Tree->GetSelection()
        wxFileName path(GetFullPath(m_selectti[i]));
        if (wxFileName::FileExists(path.GetFullPath()) || wxFileName::DirExists(path.GetFullPath())) {

            if (!PromptSaveOpenFile(wxT("文件已修改,按'是'保存后再复制,按'否'复制未保存的文件,或按'取消'跳过文件"), wxFileName(path))) { continue; }

            int j = 1;
            wxString destpath(path.GetPathWithSep() + path.GetName() + wxString::Format(_T("(%i)"), j));

            if (path.GetExt() != wxEmptyString) { destpath += _T(".") + path.GetExt(); }

            while (j < 100 && (wxFileName::FileExists(destpath) || wxFileName::DirExists(destpath))) {
                j++;
                destpath = path.GetPathWithSep() + path.GetName() + wxString::Format(_T("(%i)"), j);

                if (path.GetExt() != wxEmptyString) { destpath += _T(".") + path.GetExt(); }

            }

            if (j == 100) {
                cbMessageBox(wxT("文件或目录的副本过多"));
                continue;
            }

#ifdef __WXMSW__
            wxArrayString output;
            wxString cmdline;
            if(wxFileName::FileExists(path.GetFullPath()))
                cmdline=_T("cmd /c copy /Y \"")+path.GetFullPath()+_T("\" \"")+destpath+_T("\"");
            else
                cmdline=_T("cmd /c xcopy /S/E/Y/H/I \"")+path.GetFullPath()+_T("\" \"")+destpath+_T("\"");
            int hresult=::wxExecute(cmdline,output,wxEXEC_SYNC);
#else
            wxString cmdline = _T("/bin/cp -r -b \"") + path.GetFullPath() + _T("\" \"") + destpath + _T("\"");
            int hresult = ::wxExecute(cmdline, wxEXEC_SYNC);
#endif
            if (hresult)
                MessageBox(m_Tree, wxT("命令 '") + cmdline + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
        }
    }

    //TODO:可能比这更有效
    Refresh(m_Tree->GetRootItem());
    //TODO:在新位置重新选择项目??(在根范围之外是什么?)
}


void FileExplorer::CopyFiles(const wxString &destination, const wxArrayString &selectedfiles) {
    for (unsigned int i = 0; i < selectedfiles.Count(); i++) {
        wxString path = selectedfiles[i];
        wxFileName destpath;
        destpath.Assign(destination, wxFileName(path).GetFullName());
        if (destpath.SameAs(path))
            continue;
        if (wxFileName::FileExists(path) || wxFileName::DirExists(path)) {
            if (!PromptSaveOpenFile(wxT("文件已修改,按'是'保存后再复制,按'否'复制未保存的文件,或按'取消'跳过文件"), wxFileName(path)))
                continue;
#ifdef __WXMSW__
            wxArrayString output;
            wxString cmdline;
            if(wxFileName::FileExists(path))
                cmdline=_T("cmd /c copy /Y \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\"");
            else
                cmdline=_T("cmd /c xcopy /S/E/Y/H/I \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\"");
            int hresult=::wxExecute(cmdline,output,wxEXEC_SYNC);
#else
            int hresult = ::wxExecute(_T("/bin/cp -r -b \"") + path + _T("\" \"") + destpath.GetFullPath() + _T("\""), wxEXEC_SYNC);
#endif
            if (hresult)
                MessageBox(m_Tree, wxT("拷贝 '") + path + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
        }
    }
}

void FileExplorer::OnCopy(wxCommandEvent &/*event*/) {
    wxDirDialog dd(this, wxT("拷贝到"));
    dd.SetPath(GetFullPath(m_Tree->GetRootItem()));
    wxArrayString selectedfiles;
    m_ticount = m_Tree->GetSelections(m_selectti);
    /*不依赖TreeItemId-ater模式对话框非常重要,因为文件更新可以在后台更改文件树.*/
    for (int i = 0; i < m_ticount; i++) {
        //SINGLE: m_Tree->GetSelection()
        selectedfiles.Add(GetFullPath(m_selectti[i]));
    }

    if (dd.ShowModal() == wxID_CANCEL) { return; }

    CopyFiles(dd.GetPath(), selectedfiles);

    /*Refresh(m_Tree->GetRootItem());*/
    //TODO:如果监视不可用,使用此选项
    //TODO:在新位置重新选择项目??(如果在根作用域之外呢?)
}

void FileExplorer::MoveFiles(const wxString &destination, const wxArrayString &selectedfiles) {
    for (unsigned int i = 0; i < selectedfiles.Count(); i++) {
        wxString path = selectedfiles[i];
        wxFileName destpath;
        destpath.Assign(destination, wxFileName(path).GetFullName());
        //TODO:无法通过自身复制的日志消息.
        if (destpath.SameAs(path))
            continue;
        if (wxFileName::FileExists(path) || wxFileName::DirExists(path)) {
#ifdef __WXMSW__
            wxArrayString output;
            int hresult=::wxExecute(_T("cmd /c move /Y \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\""),output,wxEXEC_SYNC);
#else
            int hresult = ::wxExecute(_T("/bin/mv -b \"") + path + _T("\" \"") + destpath.GetFullPath() + _T("\""), wxEXEC_SYNC);
#endif
            if (hresult)
                MessageBox(m_Tree, wxT("移动 '") + path + wxT("'  失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
        }
    }
}

void FileExplorer::OnMove(wxCommandEvent &/*event*/) {
    wxDirDialog dd(this, wxT("移动到"));
    wxArrayString selectedfiles;
    m_ticount = m_Tree->GetSelections(m_selectti);
    for (int i = 0; i < m_ticount; i++)
        selectedfiles.Add(GetFullPath(m_selectti[i]));  //SINGLE: m_Tree->GetSelection()
    dd.SetPath(GetFullPath(m_Tree->GetRootItem()));
    if (dd.ShowModal() == wxID_CANCEL)
        return;
    MoveFiles(dd.GetPath(), selectedfiles);
    /*Refresh(m_Tree->GetRootItem());*/
    // TODO: 可能比这个更有效率
    //TODO: 在新位置重新选择项目(如果超出根范围怎么办？)
}

wxArrayString FileExplorer::GetSelectedPaths() {
    wxArrayString paths;
    for (int i = 0; i < m_ticount; i++) {
        //SINGLE: m_Tree->GetSelection()
        wxString path(GetFullPath(m_selectti[i]));
        paths.Add(path);
    }
    return paths;
}

void FileExplorer::OnDelete(wxCommandEvent &/*event*/) {
    m_ticount = m_Tree->GetSelections(m_selectti);
    wxArrayString as = GetSelectedPaths();
    wxString prompt = wxT("您的将要删除\n\n");
    for (unsigned int i = 0; i < as.Count(); i++)
        prompt += as[i] + wxT("\n");
    prompt += wxT("\n确定吗?");
    if (MessageBox(m_Tree, prompt, wxT("删除"), wxYES_NO) != wxID_YES)
        return;
    for (unsigned int i = 0; i < as.Count(); i++) {
        //SINGLE: m_Tree->GetSelection()
        wxString path = as[i];
        if (wxFileName::FileExists(path)) {
/*
                    EditorManager* em = Manager::Get()->GetEditorManager();
                    if(em->IsOpen(path))
                    {
                        cbMessageBox(wxT("Close file ")+path.GetFullPath()+wxT(" first"));
                        return;
                    }
*/
            if (!::wxRemoveFile(path))
                MessageBox(m_Tree, wxT("删除文件 '") + path + wxT("' 失败"));
        } else if (wxFileName::DirExists(path)) {
#ifdef __WXMSW__
            wxArrayString output;
            int hresult=::wxExecute(_T("cmd /c rmdir /S/Q \"")+path+_T("\""),output,wxEXEC_SYNC);
#else
            int hresult = ::wxExecute(_T("/bin/rm -r -f \"") + path + _T("\""), wxEXEC_SYNC);
#endif
            if (hresult)
                MessageBox(m_Tree, wxT("删除目录 '") + path + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
        }
    }
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnRename(wxCommandEvent &/*event*/) {
    //SINGLE: m_Tree->GetSelection()
    wxString path(GetFullPath(m_selectti[0]));
    if (wxFileName::FileExists(path)) {
        EditorManager *em = Manager::Get()->GetEditorManager();
        if (em->IsOpen(path)) {
            cbMessageBox(wxT("先关闭文件"));
            return;
        }
        wxTextEntryDialog te(this, wxT("新文件名:"), wxT("重命名文件"), wxFileName(path).GetFullName());
        if (te.ShowModal() == wxID_CANCEL)
            return;
        wxFileName destpath(path);
        destpath.SetFullName(te.GetValue());
        if (!::wxRenameFile(path, destpath.GetFullPath()))
            cbMessageBox(wxT("重命名失败"));
    }
    if (wxFileName::DirExists(path)) {
        wxTextEntryDialog te(this, wxT("新文件名:"), wxT("重命名文件"), wxFileName(path).GetFullName());
        if (te.ShowModal() == wxID_CANCEL)
            return;
        wxFileName destpath(path);
        destpath.SetFullName(te.GetValue());
#ifdef __WXMSW__
        wxArrayString output;
        int hresult=::wxExecute(_T("cmd /c move /Y \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\""),output,wxEXEC_SYNC);
#else
        int hresult = ::wxExecute(_T("/bin/mv \"") + path + _T("\" \"") + destpath.GetFullPath() + _T("\""), wxEXEC_SYNC);
#endif
        if (hresult)
            MessageBox(m_Tree, wxT("重命名目录 '") + path + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
    }
    /*SINGLE: m_Tree->GetSelection()*/
    Refresh(m_Tree->GetItemParent(m_selectti[0]));
}

void FileExplorer::OnExpandAll(wxCommandEvent &/*event*/) {
    /*修正多选出错问题*/
    wxTreeItemId _tree_item_id;
    if (m_Tree->HasFlag(wxTR_MULTIPLE))
    {
        wxArrayTreeItemIds _tree_item_ids;
        m_Tree->GetSelections(_tree_item_ids);
        if (_tree_item_ids.size() > 0)
        {
            /*取第一个条目*/
            _tree_item_id = _tree_item_ids[0];
        }
    }else{
        _tree_item_id=m_Tree->GetSelection();
    }

    if(!_tree_item_id){return;}

    m_Tree->ExpandAllChildren(_tree_item_id);
}

void FileExplorer::OnCollapseAll(wxCommandEvent &/*event*/) {
    m_Tree->CollapseAllChildren(m_Tree->GetSelection());
}

void FileExplorer::OnSettings(wxCommandEvent &/*event*/) {
    FileBrowserSettings fbs(m_favdirs, NULL);
    if (fbs.ShowModal() == wxID_OK) {
        size_t count = m_favdirs.GetCount();
        for (size_t i = 0; i < count; i++)
            m_Loc->Delete(0);
        m_favdirs = fbs.m_favdirs;
        count = m_favdirs.GetCount();
        for (size_t i = 0; i < count; i++)
            m_Loc->Insert(m_favdirs[i].alias, i);
    }

}

void FileExplorer::OnShowHidden(wxCommandEvent &/*event*/) {
    m_show_hidden = !m_show_hidden;
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnParseCVS(wxCommandEvent &/*event*/) {
    m_parse_cvs = !m_parse_cvs;
    //cfg->Clear();
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnParseSVN(wxCommandEvent &/*event*/) {
    m_parse_svn = !m_parse_svn;
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnParseGIT(wxCommandEvent &/*event*/) {
    m_parse_git = !m_parse_git;
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnParseHG(wxCommandEvent &/*event*/) {
    m_parse_hg = !m_parse_hg;
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnParseBZR(wxCommandEvent &/*event*/) {
    m_parse_bzr = !m_parse_bzr;
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnUpButton(wxCommandEvent &/*event*/) {

    if (m_root == _T('/')) { return; }
    wxFileName loc(m_root);
    loc.RemoveLastDir();
    /*TODO:检查这是否始终是根文件夹*/
    SetRootFolder(loc.GetFullPath());
}

void FileExplorer::OnRefresh(wxCommandEvent &/*event*/) {
    if (m_Tree->GetItemImage(m_selectti[0]) == fvsFolder) {
        //SINGLE: m_Tree->GetSelection()
        Refresh(m_selectti[0]);
    } else {
        Refresh(m_Tree->GetRootItem());
    }
}

//TODO: 必要时设置复制光标状态
void FileExplorer::OnBeginDragTreeItem(wxTreeEvent &event) {
    /*
       SetCursor(wxCROSS_CURSOR);
       if(IsInSelection(event.GetItem()))
           return;
   */
/*不要开始拖动未选中的项目*/
    if (!IsBrowsingVCSTree())
        event.Allow();
    /*m_dragtest=GetFullPath(event.GetItem());*/
    m_ticount = m_Tree->GetSelections(m_selectti);
}

bool FileExplorer::IsInSelection(const wxTreeItemId &ti) {
    for (int i = 0; i < m_ticount; i++)
        if (ti == m_selectti[i])
            return true;
    return false;
}

/*TODO:必要时结束复制光标状态*/
void FileExplorer::OnEndDragTreeItem(wxTreeEvent &event) {
/*SetCursor(wxCursor(wxCROSS_CURSOR));*/
/*只能复制到文件夹*/
    if (m_Tree->GetItemImage(event.GetItem()) != fvsFolder)
        return;
    for (int i = 0; i < m_ticount; i++) {
        wxString path(GetFullPath(m_selectti[i]));
        wxFileName destpath;
        if (!event.GetItem().IsOk())
            return;
        destpath.Assign(GetFullPath(event.GetItem()), wxFileName(path).GetFullName());
        if (destpath.SameAs(path))
            continue;
        if (wxFileName::DirExists(path) || wxFileName::FileExists(path)) {
            if (!::wxGetKeyState(WXK_CONTROL)) {
                if (wxFileName::FileExists(path))
                    if (!PromptSaveOpenFile(wxT("文件已修改,按'是'保存后再复制,按'否'复制未保存的文件,或按'取消'跳过文件"), wxFileName(path)))
                        continue;
#ifdef __WXMSW__
                wxArrayString output;
                int hresult=::wxExecute(_T("cmd /c move /Y \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\""),output,wxEXEC_SYNC);
#else
                int hresult = ::wxExecute(_T("/bin/mv -b \"") + path + _T("\" \"") + destpath.GetFullPath() + _T("\""), wxEXEC_SYNC);
#endif
                if (hresult)
                    MessageBox(m_Tree, wxT("移动目录 '") + path + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
            } else {
                if (wxFileName::FileExists(path))
                    if (!PromptSaveOpenFile(wxT("文件已修改,按'是'保存后再复制,按'否'复制未保存的文件,或按'取消'跳过文件"), wxFileName(path)))
                        continue;
#ifdef __WXMSW__
                wxArrayString output;
                wxString cmdline;
                if(wxFileName::FileExists(path))
                    cmdline=_T("cmd /c copy /Y \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\"");
                else
                    cmdline=_T("cmd /c xcopy /S/E/Y/H/I \"")+path+_T("\" \"")+destpath.GetFullPath()+_T("\"");
                int hresult=::wxExecute(cmdline,output,wxEXEC_SYNC);
#else
                int hresult = ::wxExecute(_T("/bin/cp -r -b \"") + path + _T("\" \"") + destpath.GetFullPath() + _T("\""), wxEXEC_SYNC);
#endif
                if (hresult)
                    MessageBox(m_Tree, wxT("拷贝目录 '") + path + wxT("' 失败,出现错误 ") + wxString::Format(_T("%i"), hresult));
            }
        }
        /*TODO:根据是否按住CTRL键指定"移动"或"复制"*/
        /*if(!PromptSaveOpenFile(wxT("文件已修改, 移动/复制前按\"是\"保存,按\"否\"不保存文件或者按\"取消\"终止操作"),path)) return;*/
    }
    Refresh(m_Tree->GetRootItem());
}

void FileExplorer::OnAddToProject(wxCommandEvent &/*event*/) {
    wxArrayString files;
    wxString file;
    for (int i = 0; i < m_ticount; i++) {
        file = GetFullPath(m_selectti[i]);
        if (wxFileName::FileExists(file)) { files.Add(file); }
    }
    wxArrayInt prompt;
    Manager::Get()->GetProjectManager()->AddMultipleFilesToProject(files, NULL, prompt);
    Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
}

bool FileExplorer::IsFilesOnly(wxArrayTreeItemIds tis) {
    for (size_t i = 0; i < tis.GetCount(); i++)
        if (m_Tree->GetItemImage(tis[i]) == fvsFolder) { return false; }
    return true;
}
